package com.ws.file.tool;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.ws.base.common.util.CRC32Util;
import com.ws.base.common.util.PropertiesUtil;

public class FilterSameFile {

    private final static String DELIMITER = "\\|";
    private final static String DEFAULTKEY = "only";

    // Map<filesize, Map<crc32, List<FileInfo>>>
    private Map<Long, Map<String, List<FileInfo>>> filemap;
    private Map<String, List<FileInfo>> sameFileMap;
    private FileOutputStream outputStream = null;
    private OutputStreamWriter writer = null;
    private String filepath;
    private String filterFileType;
    private Map<String, String> filterFileTypes = null;
    private String filterFolder;
    private Map<String, String> filterFolders = null;

    public FilterSameFile() {
        filepath = PropertiesUtil.getInstance().getProperty("filepath");
        filterFileType = PropertiesUtil.getInstance().getProperty(
                "filterfiletype");
        filterFolder = PropertiesUtil.getInstance().getProperty("filterFolder");

        if (null != filterFileType) {
            String[] arr = filterFileType.split(DELIMITER);
            filterFileTypes = new HashMap<String, String>();

            for (String str : arr) {
                filterFileTypes.put(str, null);
            }
        }

        if (null != filterFolder) {
            String[] arr = filterFolder.split(DELIMITER);
            filterFolders = new HashMap<String, String>();

            for (String str : arr) {
                filterFolders.put(str, null);
            }
        }

        filemap = new HashMap<Long, Map<String, List<FileInfo>>>();
        sameFileMap = new HashMap<String, List<FileInfo>>();
        writeInit(FilterSameFile.class.getResource("/").getPath());
    }

    public void execute() {
        long s1 = System.currentTimeMillis();
        traversalFolder(filepath.split(DELIMITER));
        long s2 = System.currentTimeMillis();
        System.out.println("execute time: " + ((s2 - s1) / 1000.0));
        printSameFileMap();

    }

    public FileInfo getFileInfo(File file) {
        long filesize = file.length();
        FileInfo fileInfo = new FileInfo(file.getAbsolutePath(), filesize,
                file.getName());

        return fileInfo;
    }

    public void traversalFolder(String... filepaths) {
        for (String filepath : filepaths) {
            File file = new File(filepath);

            if (file.isDirectory()) {
                tecursionFolder(file);
            }
        }
    }

    private void tecursionFolder(File file) {

        File[] files = file.listFiles();

        for (File f : files) {

            if (f.isHidden()) {
                continue;
            }

            if (f.isDirectory()) {
                System.out.println(f.getAbsolutePath());

                if (null != filterFolders
                        && filterFolders.containsKey(f.getName())) {
                    continue;
                }
                tecursionFolder(f);
            } else {

                if (null != filterFileType && isFilterFile(f)) {
                    continue;
                }
                // is file getFileInfo
                sameFileHandle(getFileInfo(f));
            }
        }
    }

    private void sameFileHandle(FileInfo fileInfo) {
        if (filemap.containsKey(fileInfo.getFileSize())) {
            Map<String, List<FileInfo>> crc32map = filemap.get(fileInfo
                    .getFileSize());
            String crc32 = CRC32Util.getInstance().getFileCRC32(
                    new File(fileInfo.getFilepath()));
            fileInfo.setCrc32(crc32);

            if (crc32map.containsKey(DEFAULTKEY)) {
                List<FileInfo> newfiles = new ArrayList<FileInfo>();
                List<FileInfo> defaulfiles = crc32map.get(DEFAULTKEY);
                String dCrc32 = CRC32Util.getInstance().getFileCRC32(
                        new File(defaulfiles.get(0).getFilepath()));
                defaulfiles.get(0).setCrc32(dCrc32);
                newfiles.addAll(defaulfiles);
                crc32map.clear();
                crc32map.put(dCrc32, newfiles);
            }

            if (crc32map.containsKey(fileInfo.getCrc32())) {
                List<FileInfo> fileInfos = crc32map.get(fileInfo.getCrc32());
                fileInfos.add(fileInfo);

                sameFileMap.put(fileInfo.getCrc32(), fileInfos);
            } else {
                List<FileInfo> fileInfos = new ArrayList<FileInfo>();
                fileInfos.add(fileInfo);
                crc32map.put(fileInfo.getCrc32(), fileInfos);
            }
        } else {
            List<FileInfo> fileInfos = new ArrayList<FileInfo>();
            Map<String, List<FileInfo>> crc32map = new HashMap<String, List<FileInfo>>();
            fileInfos.add(fileInfo);
            crc32map.put(DEFAULTKEY, fileInfos);
            filemap.put(fileInfo.getFileSize(), crc32map);
        }
    }

    private void printSameFileMap() {
        for (Entry<String, List<FileInfo>> e : sameFileMap.entrySet()) {
            write(e.getKey() + ": ");

            for (FileInfo fileInfo : e.getValue()) {
                write(fileInfo.getFilepath());
            }

            write("----------");
            write("");
        }

        closeWrite();
    }

    private void write(String str) {
        try {
            writer.write(str + "\r\n");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void writeInit(String path) {
        try {
            outputStream = new FileOutputStream(path + File.separator
                    + "sameFiles.log");
            writer = new OutputStreamWriter(outputStream,
                    Charset.forName("UTF-8"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean isFilterFile(File file) {
        boolean flag = false;
        String path = file.getAbsolutePath();
        int index = path.lastIndexOf(".");
        if (-1 != index) {
            path.substring(index);
            if (filterFileTypes.containsKey(path.substring(index))) {
                flag = true;
            }
        }

        return flag;
    }

    private void closeWrite() {
        try {
            if (null != writer) {
                writer.close();
            }
            if (null != outputStream) {
                outputStream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
